#include <map>
#include <gtest/gtest.h>
#include <cb/utility/base_meta.hpp>
#include "cb/utility/datatype_id.hpp"


class A {
    int v;
};
class B {
};
class C;

TEST(IsComplete, Meta) {
    USING_CB_NAMESPACE;
    EXPECT_TRUE(TIsComplete<A>::value) << "A should be complete";
    EXPECT_TRUE(TIsComplete<B>::value) << "B should be complete";
    EXPECT_FALSE(TIsComplete<::C>::value) << "C should not be complete";
    // This triggers a compile time error
    // EXPECT_TRUE(TIsComplete<int>::value) << "int should be complete";
}
template<typename T> class AA {
    int v;
};
using AA_I = AA<int>;

TEST(IsInstanceOfTemplate, Meta) {
    USING_CB_NAMESPACE;
    auto B1 = TIsInstanceOfTemplate<AA_I, AA>::value;
    EXPECT_TRUE(B1) << "AA<int> should be an instance of AA";
    auto B2 = TIsInstanceOfTemplate<std::string, std::basic_string>::value;
    EXPECT_TRUE(B2) << "std::string should be an instance of std::basic_string";
}

struct RandomClass {
    CB_DATATYPE_DECL("RandomClass", 0);
};
struct RandomClass2 {
    constexpr static int kDataTypeMeta = 3;
};

TEST(DataTypeMeta, Meta) {
    USING_CB_NAMESPACE;
    EXPECT_TRUE(THasDataTypeMeta<RandomClass>::value) << "RandomClass should have a data type meta";
    EXPECT_TRUE(THasDataTypeMeta<const RandomClass &>::value) << "RandomClass should have a data type meta";
    EXPECT_TRUE(THasDataTypeMeta<int>::value) << "int should have a data type meta";
    // The following line may trigger a Clang error. Just omit it, it's a bug of Clang linter.
    EXPECT_TRUE(THasDataTypeMeta<std::vector<int>>::value) << "std::vector<int> should have a data type meta";
    EXPECT_TRUE(strcmp(TGetDataTypeMeta<std::vector<int>>::value.name, "std::vector<int>") == 0) << "std::vector<int> should have a data type name of std::vector<int>";
    // C Macro sees ',' as a separator for macro parameters. The following line triggers a compile error.
    //EXPECT_TRUE(THasDataTypeMeta<std::vector<std::map<int, RandomClass>>>::value) << "std::vector<std::map<int, RandomClass>> should have a data type meta";
    EXPECT_TRUE((THasDataTypeMeta<std::vector<std::map<int, RandomClass>>>::value)) << "std::vector<std::map<int, RandomClass>> should have a data type meta";
    // They should trigger compile errors
    // EXPECT_FALSE(THasDataTypeMeta<RandomClass2>::value) << "RandomClass2 should not have a data type meta";
    // EXPECT_FALSE(THasDataTypeMeta<const RandomClass2&>::value) << "RandomClass2 should not have a data type meta";
}
